﻿using BingFa.Entity;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace BingFa.UI.Models
{
    public class IndexModel
    {
        /// <summary>
        /// 未做并发处理
        /// </summary>
        /// <param name="i"></param>
        public IndexModel(string i)
        {
            SubMitOrder_3();
        }

        /// <summary>
        /// EF 专属并发处理
        /// </summary>
        public IndexModel()
        {
            SubMitOrder();
        }
        /// <summary>
        /// 通用并发处理
        /// </summary>
        /// <param name="i"></param>
        public IndexModel(int i)
        {
            SubMitOrder_2();
        }

        #region 未做并发处理
        /// <summary>
        /// 模仿一个减少库存操作  不加并发控制
        /// </summary>
        public void SubMitOrder_3()
        {
            int productId = 1;

            using (BingFaTestEntities12 context = new BingFaTestEntities12())
            {
                var InventoryLogDbSet = context.InventoryLog;
                var InventoryDbSet = context.Inventory;//库存表

                using (var Transaction = context.Database.BeginTransaction())
                {
                    //减少库存操作
                    var Inventory_Mol = InventoryDbSet.Where(A => A.ProductId == productId).FirstOrDefault();//库存对象
                    Inventory_Mol.ProductCount = Inventory_Mol.ProductCount - 1;
                    int A4 = context.SaveChanges();
                    //插入日志
                    InventoryLog LogModel = new InventoryLog()
                    {
                        Title = "插入一条数据，用于计算是否发生并发",

                    };
                    InventoryLogDbSet.Add(LogModel);
                    context.SaveChanges();
                    //1.5  模拟耗时
                    Thread.Sleep(500); //消耗半秒钟
                    Transaction.Commit();
                }

            }
        }
        #endregion

        #region EF专属并发处理模式      
        public void SubMitOrder()
        {
            int C = LockForEF();
            //1.5  模拟耗时
            //Thread.Sleep(500); //消耗半秒钟
            int retry = 10;
            while (C<0 && retry > 0)
            {
                retry--;
                C= LockForEF();
            }
        }
        /// <summary>
        /// 模仿一个减少库存操作  EF专属并发处理模式
        /// </summary>
        public int LockForEF()
        {
            int productId = 1;
            int C = 0;
            using (SaveChangesForBF context = new SaveChangesForBF())
            {
                var InventoryLogDbSet = context.InventoryLog;
                var InventoryDbSet = context.Inventory;//库存表

                using (var Transaction = context.Database.BeginTransaction())
                {
                    //减少库存操作
                    var Inventory_Mol = InventoryDbSet.Where(A => A.ProductId == productId).FirstOrDefault();//库存对象
                    Inventory_Mol.ProductCount = Inventory_Mol.ProductCount - 1;
                    C = context.SaveChanges();
                    //插入日志
                    InventoryLog LogModel = new InventoryLog()
                    {
                        Title = "插入一条数据，用于计算是否发生并发",
                        
                    };
                    InventoryLogDbSet.Add(LogModel);
                    context.SaveChanges();
                    //1.5  模拟耗时
                    //Thread.Sleep(500); //消耗半秒钟
                    Transaction.Commit();
                }

            }
            return C;
        }
        #endregion

        #region 通用并发处理模式 存储过程实现
        /// <summary>
        /// 存储过程实现
        /// </summary>
        public void SubMitOrder_2()
        {
            int productId = 1;
            bool bol = LockForPorcduce(productId);
            //1.5  模拟耗时
            Thread.Sleep(500); //消耗半秒钟
            int retry = 10;
            while (!bol && retry > 0)
            {
                retry--;
                LockForPorcduce(productId);
            }
        }


        private bool LockForPorcduce(int ProductId)
        {
            using (BingFaTestEntities12 context = new BingFaTestEntities12())
            {
                SqlParameter[] parameters = {
                    new SqlParameter("@ProductId", SqlDbType.Int),
                    new SqlParameter("@IsSuccess", SqlDbType.Bit)
                    };
                parameters[0].Value = ProductId;
                parameters[1].Direction = ParameterDirection.Output;
                var data = context.Database.ExecuteSqlCommand("exec LockProc @ProductId,@IsSuccess output", parameters);
                string n2 = parameters[1].Value.ToString();
                if (n2 == "True")
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        #endregion
    }
}